home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks ’87 / Source ƒ.sit / Source ƒ / C ƒ / CITADEL BBS 'C' SRC / MSG.C < prev    next >
C/C++ Source or Header  |  1987-01-14  |  51KB  |  1,735 lines

  1. /************************************************************************/
  2. /*                msg.c                    */
  3. /*                                    */
  4. /*    Message handling for Citadel bulletin board system        */
  5. /************************************************************************/
  6.  
  7. /************************************************************************/
  8. /*                history                 */
  9. /*                                    */
  10. /* 86Aug15 HAW    Large chunk of History deleted due to space problems.    */
  11. /* 84Mar29 HAW Start upgrade to BDS C 1.50a, identify _spr problem.    */
  12. /* 83Mar03 CrT & SB   Various bug fixes...                */
  13. /* 83Feb27 CrT    Save private mail for sender as well as recipient.    */
  14. /* 83Feb23    Various.  transmitFile() won't drop first char on WC... */
  15. /* 82Dec06 CrT    2.00 release.                        */
  16. /* 82Nov05 CrT    Stream retrieval.  Handles messages longer than MAXTEXT.*/
  17. /* 82Nov04 CrT    Revised disk format implemented.            */
  18. /* 82Nov03 CrT    Individual history begun.  General cleanup.        */
  19. /************************************************************************/
  20.  
  21. #include "ctdl.h"
  22.  
  23. #define TEST_SYS
  24.  
  25. /************************************************************************/
  26. /*                contents                */
  27. /*                                    */
  28. /*    aideMessage()        saves auto message in Aide>        */
  29. /*    dGetWord()        reads a word off disk            */
  30. /*    doActualWrite()     to allow two message files for bkp    */
  31. /*    doFlush()        writes out to specified msg file    */
  32. /*  #    dPree()         special utility for dPrintf & mWCprintf */
  33. /*  #    dPrintf()        printf() that writes to disk        */
  34. /*    fakeFullCase()        converts uppercase message to mixed case*/
  35. /*    flushMsgBuf()        wraps up message-to-disk store        */
  36. /*    getMessage()        load message into RAM            */
  37. /*    getMsgChar()        returns successive chars off disk    */
  38. /*    getMsgStr()        reads a string out of message.buf    */
  39. /*    getWord()        gets one word from message buffer    */
  40. /*    mAbort()        checks for user abort of typeout    */
  41. /*    makeMessage()        menu-level message-entry routine    */
  42. /*    mFormat()        formats a string to modem and console    */
  43. /*    mPeek()         sysop debugging tool--shows ctdlmsg.sys */
  44. /*  #    mPrintf()        writes a line to modem & console    */
  45. /*  #    mWCprintf()        special mPrintf for WC transfers    */
  46. /*    noteLogMessage()    enter message into log record        */
  47. /*    noteMessage()        enter message into current room     */
  48. /*    note2Message()        noteMessage() local            */
  49. /*    printMessage()        prints a message on modem & console    */
  50. /*    pullIt()        sysop special message-removal routine    */
  51. /*    putLong()        puts a long integer to file        */
  52. /*    putMessage()        write message to disk            */
  53. /*    putMsgChar()        writes successive message chars to disk */
  54. /*    putNetMessage()     write net message to disk        */
  55. /*    putWord()        writes one word to modem & console    */
  56. /*    showMessages()        menu-level show-roomful-of-messages fn    */
  57. /*    startAt()        setup to read a message off disk    */
  58. /*    unGetMsgChar()        return a char to getMsgChar()        */
  59. /*                                    */
  60. /*    # -- functions that will give problems when porting        */
  61. /************************************************************************/
  62.  
  63. /************************************************************************/
  64. /*           External variable declarations in MSG.C        */
  65. /************************************************************************/
  66. static unsigned char GMCCache;    /* To unGetMsgChar() into        */
  67. struct msgB         msgBuf;    /* The -sole- message buffer        */
  68. struct msgB         tempMess;    /* For held messages            */
  69. FILE             *msgfl;    /* file descriptor for the msg file    */
  70. FILE             *msgfl2;    /* disk based backup msg file        */
  71. unsigned char         crtColumn; /* current position on screen        */
  72. char             outFlag = OUTOK;    /* will be one of the above    */
  73. static char         pullMessage = FALSE;/* true to pull current message*/
  74. static char         journalMessage = FALSE;
  75. unsigned         pulledMLoc;/* loc of pulled message        */
  76. ulong             pulledMId = 0l;    /* id of message to be pulled    */
  77. char             heldMessage;
  78. label             oldTarget;
  79. char             jrnlFile[100] = "";
  80. char           *NoNetRoomPrivs = "You do not have net privileges";
  81.  
  82. #ifdef _C86_BIG     /*Black magic stuff */
  83. static int         j;
  84. static unsigned char **starr[5];
  85. #endif
  86.  
  87. struct mBuf {
  88.     unsigned char sectBuf[SECTSIZE];
  89.     int       thisChar;
  90.     unsigned      thisSector;
  91.     int       oldChar;
  92.     unsigned      oldSector;
  93. } ;
  94.  
  95. static struct mBuf mFile1, mFile2;
  96.  
  97. /************************************************************************/
  98. /*           External variable definitions for MSG.C        */
  99. /************************************************************************/
  100. extern struct config    cfg;        /* Configuration variables    */
  101. extern struct lTable    *logTab;    /* The people            */
  102. extern struct logBuffer logBuf;     /* Buffer for the pippuls    */
  103. extern struct aRoom    roomBuf;    /* Room buffer            */
  104. extern struct rTable    roomTab[MAXROOMS];
  105. extern struct netTable    *netTab;
  106. extern struct netBuffer netBuf;
  107. extern FILE        *upfd;
  108. extern int        thisRoom;    /* Current room         */
  109. extern int        thisNet;    /* Current node in use        */
  110. extern int        thisLog;    /* Current log position     */
  111. extern int        outPut;
  112. extern char        *strFile;
  113. extern char        exChar;
  114. extern char        echo;        /* Output flag            */
  115. extern char        echoChar;
  116. extern char        loggedIn;    /* Logged in flag        */
  117. extern char        aide;        /* aide?            */
  118. extern char        sendTime;    /* User wants to know time    */
  119. extern char        whichIO;    /* Who gets output?        */
  120. extern unsigned char    termWidth;    /* How big's the terminal?    */
  121. extern char        prevChar;    /* Output's evil purposes    */
  122. extern char        expert;     /* Expert?            */
  123. extern char        inNet;
  124. extern char        *R_SH_MARK;
  125. extern char        usingWCprotocol;/* Flag             */
  126. extern char        haveCarrier;    /* Flag             */
  127. extern char        onConsole;    /* Flag             */
  128. extern char        oldToo;     /* Print old msg on New request?*/
  129.  
  130. long getNumber();
  131.  
  132. /************************************************************************/
  133. /*           External function definitions for MSG.C        */
  134. /************************************************************************/
  135. char          BBSCharReady();
  136. char          iChar();
  137. char          toUpper();
  138. char          putMessage();
  139. unsigned char getMsgChar();
  140.  
  141. /************************************************************************/
  142. /*    aideMessage() saves auto message in Aide>            */
  143. /************************************************************************/
  144. aideMessage(noteDeletedMessage)
  145. char noteDeletedMessage;
  146. {
  147.     int ourRoom;
  148.  
  149.     /* Ensures not a net message    */
  150.     msgBuf.mboname[0] = 0;
  151.     msgBuf.mborig[0]  = 0;
  152.     msgBuf.mbsrcId[0] = 0;
  153.  
  154.     /* message is already set up in msgBuf.mbtext */
  155.     putRoom(ourRoom = thisRoom);
  156.     getRoom(AIDEROOM);
  157.  
  158.     strCpy(msgBuf.mbauth, "Citadel");
  159.     msgBuf.mbto[0] = '\0';
  160.     if (putMessage())    noteMessage(NULL, ERROR);
  161.  
  162.     if (noteDeletedMessage)   {
  163.     note2Message(pulledMId, pulledMLoc);
  164.     }
  165.  
  166.     putRoom(AIDEROOM);
  167.     noteRoom();
  168.     getRoom(ourRoom);
  169. }
  170.  
  171. /************************************************************************/
  172. /*    deleteMessage() deletes message for pullIt()            */
  173. /************************************************************************/
  174. deleteMessage(m)
  175. int m;
  176. {
  177.     int i;
  178.  
  179.     /* record vital statistics for possible insertion elsewhere: */
  180.     mPrintf("elete message\n ");
  181.     pulledMLoc = roomBuf.msg[m].rbmsgLoc;
  182.     pulledMId  = roomBuf.msg[m].rbmsgNo ;
  183.  
  184.     if (thisRoom == AIDEROOM)    return TRUE;
  185.  
  186.     /* return emptied slot: */
  187.     for (i = m;  i > 0;  i--) {
  188.     roomBuf.msg[i].rbmsgLoc      = roomBuf.msg[i - 1].rbmsgLoc;
  189.     roomBuf.msg[i].rbmsgNo         = roomBuf.msg[i - 1].rbmsgNo ;
  190.     }
  191.     roomBuf.msg[0].rbmsgNo   = 0l;    /* mark new slot at end as free */
  192.     roomBuf.msg[0].rbmsgLoc  = 0;      /* mark new slot at end as free */
  193.  
  194.     /* store revised room to disk before we forget...    */
  195.     noteRoom();
  196.     putRoom(thisRoom);
  197.  
  198.     /* note in Aide>: */
  199.     sPrintf(msgBuf.mbtext, "Following message from %s deleted by %s:",
  200.    (msgBuf.mbauth[0]) ? msgBuf.mbauth : "<anonymous>", logBuf.lbname);
  201.     aideMessage( /* noteDeletedMessage== */ TRUE);
  202.     return TRUE;
  203. }
  204.  
  205. /************************************************************************/
  206. /*    dGetWord() fetches one word from current message, off disk    */
  207. /*    returns TRUE if more words follow, else FALSE            */
  208. /************************************************************************/
  209. char dGetWord(dest, lim)
  210. char *dest;
  211. int  lim;
  212. {
  213.     char c;
  214.  
  215.     --lim;    /* play it safe */
  216.  
  217.     /* pick up any leading blanks: */
  218.     for (c = getMsgChar();   c == ' '  &&  c && lim;   c = getMsgChar()) {
  219.     if (lim) { *dest++ = c;   lim--; }
  220.     }
  221.  
  222.     /* step through word: */
  223.     for (         ;  c != ' ' && c && lim;   c = getMsgChar()) {
  224.     if (lim) { *dest++ = c;   lim--; }
  225.     }
  226.  
  227.     /* trailing blanks: */
  228.     for (         ;   c == ' ' && c && lim;   c = getMsgChar()) {
  229.     if (lim) { *dest++ = c;   lim--; }
  230.     }
  231.  
  232.     if (c)  unGetMsgChar(c);    /* took one too many    */
  233.  
  234.     *dest = '\0';        /* tie off string    */
  235.  
  236.     return  c;
  237. }
  238.  
  239. /************************************************************************/
  240. /*    doActualWrite() to allow automatic bkp of msg file from RAM.    */
  241. /************************************************************************/
  242. doActualWrite(whichmsg, mFile, c)
  243. FILE *whichmsg;
  244. struct mBuf *mFile;
  245. unsigned c;
  246. {
  247.     ulong temp;
  248.     int toReturn = 0;
  249.  
  250.     if (mFile->sectBuf[mFile->thisChar] == 0xFF)  {
  251.     /* obliterating a msg    */
  252.     toReturn = 1;
  253.     }
  254.  
  255.     mFile->sectBuf[mFile->thisChar]   = c;
  256.  
  257.     mFile->thisChar    = ++mFile->thisChar % SECTSIZE;
  258.  
  259.     if (mFile->thisChar == 0) { /* time to write sector out and get next: */
  260.     temp = mFile->thisSector;
  261.     temp *= SECTSIZE;
  262.     fseek(whichmsg, temp, 0);
  263.     crypte(mFile->sectBuf, SECTSIZE, 0);
  264.     if (fwrite(mFile->sectBuf, SECTSIZE, 1, whichmsg) != 1) {
  265.         crashout("?putMsgChar-write fail");
  266.     }
  267.  
  268.     mFile->thisSector = ++mFile->thisSector % cfg.maxMSector;
  269.     temp = mFile->thisSector;
  270.     temp *= SECTSIZE;
  271.     fseek(whichmsg, temp, 0);
  272.     if (fread(mFile->sectBuf, SECTSIZE, 1, whichmsg) != 1) {
  273.         crashout("?putMsgChar-read fail");
  274.     }
  275.     crypte(mFile->sectBuf, SECTSIZE, 0);
  276.     }
  277.     return  toReturn;
  278. }
  279.  
  280. /************************************************************************/
  281. /*    doFlush() do actual writeup for specified msg file        */
  282. /************************************************************************/
  283. doFlush(whichmsg, mFile)
  284. FILE *whichmsg;
  285. struct mBuf *mFile;
  286. {
  287.     long int s;
  288.  
  289.     s = mFile->thisSector;
  290.     s *= SECTSIZE;
  291.     fseek(whichmsg, s, 0);
  292.     crypte(mFile->sectBuf, SECTSIZE, 0);
  293.     if (fwrite(mFile->sectBuf, SECTSIZE, 1, whichmsg) != 1) {
  294.     crashout("?ctdlmsg.sys write fail");
  295.     }
  296.     crypte(mFile->sectBuf, SECTSIZE, 0);
  297.     fflush(whichmsg);
  298. }
  299.  
  300.  
  301. /************************************************************************/
  302. /*    dPree() Special utility for dPrintf() and mWCprintf        */
  303. /************************************************************************/
  304. dPree(func, start, length)
  305. int func;
  306. char *start;
  307. unsigned length;
  308. {
  309.     int (*fn)(), putMsgChar(), sendWCChar();
  310.  
  311.     switch (func) {
  312.     case 0:  fn = &putMsgChar; break;
  313.     case 1:  fn = &sendWCChar; break;
  314.     default: crashout("dPree error value!");
  315.     }
  316.     while (length--)
  317.     if ((*fn)(*start++) == ERROR) return ERROR;
  318.  
  319. }
  320.  
  321. /************************************************************************/
  322. /*    dPrintf() write from format+args to disk            */
  323. /************************************************************************/
  324. dPrintf(format, args)
  325. unsigned      args;
  326. unsigned char *format;
  327. {
  328.     int  dPree();
  329.  
  330.     _fmtout(&dPree, 0, format, &args);
  331. }
  332.  
  333. /************************************************************************/
  334. /*    fakeFullCase() converts a message in uppercase-only to a    */
  335. /*    reasonable mix.  It can't possibly make matters worse...    */
  336. /*    Algorithm: First alphabetic after a period is uppercase, all    */
  337. /*    others are lowercase, excepting pronoun "I" is a special case.    */
  338. /*    We assume an imaginary period preceding the text.        */
  339. /************************************************************************/
  340. fakeFullCase(text)
  341. char *text;
  342. {
  343.     char toLower(), toUpper();
  344.     char *c;
  345.     char lastWasPeriod;
  346.     char state;
  347.  
  348.     for(lastWasPeriod=TRUE, c=text;   *c;  c++) {
  349.     if (
  350.         *c != '.'
  351.         &&
  352.         *c != '?'
  353.         &&
  354.         *c != '!'
  355.     ) {
  356.         if (isAlpha(*c)) {
  357.         if (lastWasPeriod)    *c    = toUpper(*c);
  358.         else            *c    = toLower(*c);
  359.         lastWasPeriod    = FALSE;
  360.         }
  361.     } else {
  362.         lastWasPeriod    = TRUE ;
  363.     }
  364.     }
  365.  
  366.     /* little state machine to search for ' i ': */
  367. #define NUTHIN        0
  368. #define FIRSTBLANK    1
  369. #define BLANKI        2
  370.     for (state=NUTHIN, c=text;    *c;  c++) {
  371.     switch (state) {
  372.     case NUTHIN:
  373.         if (isSpace(*c))    state    = FIRSTBLANK;
  374.         else        state    = NUTHIN    ;
  375.         break;
  376.     case FIRSTBLANK:
  377.         if (*c == 'i')    state    = BLANKI    ;
  378.         else        state    = NUTHIN    ;
  379.         break;
  380.     case BLANKI:
  381.         if (isSpace(*c))    state    = FIRSTBLANK;
  382.         else        state    = NUTHIN    ;
  383.  
  384.         if (!isAlpha(*c))    *(c-1)    = 'I';
  385.         break;
  386.     }
  387.     }
  388. }
  389.  
  390. struct msgTemp {
  391.     ulong idnum;
  392.     unsigned loc;
  393. } ;
  394.  
  395. /************************************************************************/
  396. /*    findHighestNative() Finds highest native message in a room    */
  397. /************************************************************************/
  398. ulong findHighestNative(i)
  399. {
  400.     int ourSlot, rover;
  401.     int msgSort();
  402.     ulong ourHighest;
  403.     struct msgTemp temp[MSGSPERRM];
  404.  
  405.     if (!roomTab[i].rtflags.INUSE)
  406.     return 0l;
  407.  
  408.     ourSlot = thisRoom;
  409.     getRoom(i);
  410.     copy_array(roomBuf.msg, temp);
  411.     qsort(temp, MSGSPERRM, sizeof temp[0], msgSort);
  412.  
  413.     ourHighest = 0l;
  414.     for (rover = 0; rover < MSGSPERRM; rover++) {
  415.     if (temp[rover].idnum != 0l &&
  416.                findMessage(temp[rover].loc, temp[rover].idnum))
  417.         if (strCmpU(msgBuf.mbaddr, R_SH_MARK) == SAMESTRING) {
  418.         ourHighest = temp[rover].idnum;
  419.         break;
  420.         }
  421.     }
  422.     getRoom(ourSlot);
  423.     return ourHighest;
  424. }
  425.  
  426. msgSort(s1, s2)
  427. struct msgTemp *s1, *s2;
  428. {
  429.     if (s1->rbmsgNo < s2->rbmsgNo) return 1;
  430.     if (s1->rbmsgNo > s2->rbmsgNo) return -1;
  431.     return 0;
  432. }
  433.  
  434. /************************************************************************/
  435. /*    flushMsgBuf() wraps up writing a message to disk, takes into    */
  436. /*              account 2nd msg file if necessary         */
  437. /************************************************************************/
  438. flushMsgBuf()
  439. {
  440.     doFlush(msgfl, &mFile1);
  441.     if (cfg.mirror)
  442.     doFlush(msgfl2, &mFile2);
  443. }
  444.  
  445. /************************************************************************/
  446. /*    getMessage() reads a message off disk into RAM.         */
  447. /*    a previous call to setUp has specified the message.        */
  448. /************************************************************************/
  449. getMessage()
  450. {
  451.     unsigned char c;
  452.  
  453.     /* clear msgBuf out */
  454.     msgBuf.mbauth[ 0]    = '\0';
  455.     msgBuf.mbtime[ 0]    = '\0';
  456.     msgBuf.mbdate[ 0]    = '\0';
  457.     msgBuf.mborig[ 0]    = '\0';
  458.     msgBuf.mboname[0]    = '\0';
  459.     msgBuf.mbroom[ 0]    = '\0';
  460.     msgBuf.mbsrcId[0]    = '\0';
  461.     msgBuf.mbtext[ 0]    = '\0';
  462.     msgBuf.mbaddr[ 0]    = '\0';
  463.     msgBuf.mbto[   0]    = '\0';
  464.  
  465.     do c = getMsgChar(); while (c != 0xFF);    /* find start of msg    */
  466.  
  467.     msgBuf.mbheadChar    = mFile1.oldChar;     /* record location     */
  468.     msgBuf.mbheadSector = mFile1.oldSector;
  469.  
  470.     getMsgStr(msgBuf.mbId, NAMESIZE);
  471.  
  472.     do    {
  473.     c = getMsgChar();
  474.     switch (c) {
  475.     case 'A':    getMsgStr(msgBuf.mbauth,  NAMESIZE);    break;
  476.     case 'D':    getMsgStr(msgBuf.mbdate,  NAMESIZE);    break;
  477.     case 'C':    getMsgStr(msgBuf.mbtime,  NAMESIZE);    break;
  478.     case 'M':    /* just exit -- we'll read off disk */    break;
  479.     case 'N':    getMsgStr(msgBuf.mboname, NAMESIZE);    break;
  480.     case 'O':    getMsgStr(msgBuf.mborig,  NAMESIZE);    break;
  481.     case 'R':    getMsgStr(msgBuf.mbroom,  NAMESIZE);    break;
  482.     case 'S':    getMsgStr(msgBuf.mbsrcId, NAMESIZE);    break;
  483.     case 'T':    getMsgStr(msgBuf.mbto,      NAMESIZE);    break;
  484.     case 'Q':    getMsgStr(msgBuf.mbaddr,  NAMESIZE);    break;
  485.     default:
  486.         getMsgStr(msgBuf.mbtext, MAXTEXT);    /* discard unknown field  */
  487.         msgBuf.mbtext[0]    = '\0';
  488.         break;
  489.     }
  490.     } while (c != 'M'  &&  isAlpha(c));
  491. }
  492.  
  493. /************************************************************************/
  494. /*    getMsgChar() returns sequential chars from message on disk    */
  495. /************************************************************************/
  496. unsigned char getMsgChar()
  497. {
  498.     long work;
  499.     char toReturn;
  500.  
  501.     if (GMCCache) {    /* someone did an unGetMsgChar() --return it    */
  502.     toReturn= GMCCache;
  503.     GMCCache= '\0';
  504.     return toReturn;
  505.     }
  506.  
  507.     mFile1.oldChar     = mFile1.thisChar;
  508.     mFile1.oldSector   = mFile1.thisSector;
  509.  
  510.     toReturn    = mFile1.sectBuf[mFile1.thisChar];
  511.  
  512.     mFile1.thisChar    = ++mFile1.thisChar % SECTSIZE;
  513.     if (mFile1.thisChar == 0) {
  514.     /* time to read next sector in: */
  515.     mFile1.thisSector  = ++mFile1.thisSector % cfg.maxMSector;
  516.     work = mFile1.thisSector;
  517.     work *= SECTSIZE;
  518.     fseek(msgfl, work, 0);
  519.     if (fread(mFile1.sectBuf, SECTSIZE, 1, msgfl) != 1) {
  520.         crashout("?nextMsgChar-read fail");
  521.     }
  522.     crypte(mFile1.sectBuf, SECTSIZE, 0);
  523.     }
  524.     return(toReturn);
  525. }
  526.  
  527. /************************************************************************/
  528. /*    getMsgStr() reads a string from message.buf            */
  529. /************************************************************************/
  530. getMsgStr(dest, lim)
  531. char *dest;
  532. int  lim;
  533. {
  534.     char c;
  535.  
  536.     while (c = getMsgChar()) {        /* read the complete string    */
  537.     if (lim) {            /* if we have room then     */
  538.         lim--;
  539.         *dest++ = c;        /* copy char to buffer        */
  540.     }
  541.     }
  542.     *dest = '\0';            /* tie string off with null    */
  543. }
  544.  
  545. /************************************************************************/
  546. /*    getWord() fetches one word from current message         */
  547. /************************************************************************/
  548. int getWord(dest, source, offset, lim)
  549. char *dest, *source;
  550. int  lim, offset;
  551. {
  552.     int i, j;
  553.  
  554.     /* skip leading blanks if any */
  555.     for (i = 0;  source[offset+i] ==' ' && i < lim;  i++);
  556.  
  557.     /* step over word */
  558.     for (;
  559.  
  560.      source[offset+i]   != ' '    &&
  561.      i            <  lim    &&
  562.      source[offset+i]   != 0;
  563.  
  564.      i++
  565.     );
  566.  
  567.     /* pick up any trailing blanks */
  568.     for (;  source[offset+i]==' ' && i<lim;  i++);
  569.  
  570.     /* copy word over */
  571.     for (j = 0; j < i; j++)  dest[j] = source[offset+j];
  572.     dest[j] = 0;    /* null to tie off string */
  573.  
  574.     return(offset+i);
  575. }
  576.  
  577. /************************************************************************/
  578. /*    mAbort() returns TRUE if the user has aborted typeout        */
  579. /*    Globals modified:    outFlag                 */
  580. /************************************************************************/
  581. char mAbort()
  582. {
  583.     char c, toReturn, oldEcho;
  584.  
  585.     /* Check for abort/pause from user */
  586.     if (outFlag == IMPERVIOUS)
  587.     toReturn    = FALSE;
  588.     else if (!BBSCharReady()) {
  589.     if (haveCarrier && !gotCarrier())
  590.         modIn();        /* Let modIn() report the problem    */
  591.     toReturn    = FALSE;
  592.     } else {
  593.     oldEcho  = echo;
  594.     echo     = NEITHER;
  595.     echoChar = 0;
  596.     c = toUpper(iChar());
  597.     switch (c) {
  598.     case XOFF:
  599.     case 'P':                    /*    pause:          */
  600.         c = iChar();                /* wait to resume */
  601.         if (toLower(c) == 'd' && aide)
  602.         pullMessage = TRUE;
  603.         else if (toLower(c) == 'j' && TheSysop())
  604.         journalMessage = TRUE;
  605.         toReturn      = FALSE;
  606.         break;
  607.     case 'J':                    /* jump paragraph:*/
  608.         outFlag    = OUTPARAGRAPH;
  609.         toReturn    = FALSE;
  610.         break;
  611.     case 'N':                    /* next:          */
  612.         outFlag    = OUTNEXT;
  613.         toReturn    = TRUE;
  614.         break;
  615.     case 'S':                    /* skip:          */
  616.         outFlag    = OUTSKIP;
  617.         toReturn    = TRUE;
  618.         break;
  619.     default:
  620.         toReturn    = FALSE;
  621.         break;
  622.     }
  623.     echo    = oldEcho;
  624.     }
  625.     return toReturn;
  626. }
  627.  
  628. getRecipient(lBuf, logNo)
  629. int *logNo;
  630. struct logBuffer *lBuf;
  631. {
  632.     if (thisRoom != MAILROOM)  {
  633.     msgBuf.mbto[0] = 0;        /* Zero recipient    */
  634.     return TRUE;
  635.     }
  636.  
  637.     if (msgBuf.mbto[0] == 0) {
  638.     if (!loggedIn || (!aide && cfg.noMail)) {
  639.         strCpy(msgBuf.mbto, "Sysop");
  640.         mPrintf(" (private mail to 'sysop')\n ");
  641.         return TRUE;
  642.     }
  643.  
  644.     getNormStr("recipient", msgBuf.mbto, NAMESIZE, ECHO);
  645.     if (strLen(msgBuf.mbto) == 0) return FALSE;
  646.     }
  647.  
  648.     if (!msgBuf.mbaddr[0]) {
  649.     *logNo     = findPerson(msgBuf.mbto, lBuf);
  650.     if ((*logNo == ERROR &&
  651.           (strCmpU(msgBuf.mbto, "Citadel") != SAMESTRING ||
  652.            !aide    ||      !onConsole) &&
  653.          hash(msgBuf.mbto) != hash("Sysop")) ||
  654.          strLen(msgBuf.mbto) == 0) {
  655.         if (!inNet) mPrintf("No '%s' known", msgBuf.mbto);
  656.         return FALSE;
  657.     }
  658.     if (hash(msgBuf.mbto) != hash("Sysop") &&
  659.         strCmpU(msgBuf.mbto, "Citadel") != SAMESTRING)
  660.         strCpy(msgBuf.mbto, lBuf->lbname);    /* Get "true" rep.  */
  661.     if (strCmp(msgBuf.mbto, logBuf.lbname) == SAMESTRING) {
  662.         if (!inNet) mPrintf("Can't send mail to yourself, silly!");
  663.         return FALSE;
  664.     }
  665.     }
  666.     return TRUE;
  667. }
  668.  
  669. /************************************************************************/
  670. /*    replyMessage() reply to a Mail> message             */
  671. /************************************************************************/
  672. replyMessage()
  673. {
  674.     label who;
  675.  
  676.     strCpy(who, msgBuf.mbauth);
  677.     setmem(&msgBuf, sizeof msgBuf, 0);
  678.     strCpy(msgBuf.mbto, who);
  679.     return procMessage(FALSE);
  680. }
  681.  
  682. /************************************************************************/
  683. /*    hldMessage() handles held messages                */
  684. /************************************************************************/
  685. hldMessage()
  686. {
  687.     if (!heldMessage) {
  688.     mPrintf(" \n No message in the Hold buffer!\007\n ");
  689.     return FALSE;
  690.     }
  691.     heldMessage = FALSE;
  692.     copy_struct(tempMess, msgBuf);
  693.     procMessage(FALSE);
  694. }
  695.  
  696. /************************************************************************/
  697. /*    makeMessage is menu-level routine to enter a message        */
  698. /*    Return: TRUE if message saved else FALSE            */
  699. /************************************************************************/
  700. makeMessage(uploading)
  701. char uploading;
  702. {
  703.     zero_struct(msgBuf);
  704.     procMessage(uploading);
  705. }
  706.  
  707. /************************************************************************/
  708. /*    procMessage is menu-level routine to enter a message        */
  709. /************************************************************************/
  710. procMessage(uploading)
  711. char uploading;
  712. {
  713.     char         *pc, allUpper;
  714.     struct logBuffer lBuf;
  715.     int          logNo;
  716.  
  717.     if (loggedIn)
  718.     strCpy(msgBuf.mbauth, logBuf.lbname);
  719.  
  720.     if (!getRecipient(&lBuf, &logNo))
  721.     return ;
  722.  
  723.     if (getText(uploading) == TRUE) {
  724.     for (pc=msgBuf.mbtext, allUpper=TRUE;    *pc && allUpper;  pc++) {
  725.         if (toUpper(*pc) != *pc)   allUpper = FALSE;
  726.     }
  727.     if (allUpper)    fakeFullCase(msgBuf.mbtext, MAXTEXT);
  728.  
  729.     if (putMessage()) noteMessage(&lBuf, logNo);
  730.     return TRUE;
  731.     }
  732.     return FALSE;
  733. }
  734.  
  735. /************************************************************************/
  736. /*    mFormat() formats a string to modem and console         */
  737. /************************************************************************/
  738. mFormat(string)
  739. char *string;
  740. #define MAXWORD 256    /* maximum length of a word */
  741. {
  742.     char wordBuf[MAXWORD];
  743.     char mAbort();
  744.     int  i;
  745.  
  746.     for (i = 0;  string[i] && (outFlag == OUTOK      ||
  747.                  outFlag == IMPERVIOUS ||
  748.                  outFlag == OUTPARAGRAPH);    ) {
  749.     i = getWord(wordBuf, string, i, MAXWORD);
  750.     putWord(wordBuf);
  751.     if (mAbort()) return;
  752.     }
  753. }
  754.  
  755. /************************************************************************/
  756. /*    moveMessage() Moves a message for pullIt()            */
  757. /************************************************************************/
  758. moveMessage(m)
  759. int m;
  760. {
  761.     label blah;
  762.     int   i, roomTarg, r1, ourRoom;
  763.     int   curRoom;
  764.     int   roomExists(), partialExist();
  765.     char  prompt[55];
  766.  
  767.     curRoom = thisRoom;
  768.     mPrintf("ove message\n ");
  769.     sPrintf(prompt, "where (C/R = '%s'): ", oldTarget);
  770.     getString(prompt, blah, 20, FALSE, ECHO);
  771.     if (!onLine()) return FALSE;
  772.     if (strLen(blah) == 0)
  773.     strCpy(blah, oldTarget);
  774.     if ((roomTarg = roomCheck(roomExists, blah)) == ERROR) {
  775.     if ((roomTarg = roomCheck(partialExist, blah)) == ERROR) {
  776.         mPrintf("'%s' does not exist.", blah);
  777.         return FALSE;
  778.     }
  779.     else {
  780.         thisRoom = roomTarg;
  781.         if ((r1 = roomCheck(partialExist, blah)) != ERROR) {
  782.         thisRoom = curRoom;
  783.         mPrintf("'%s' is not a unique string.", blah);
  784.         return FALSE;
  785.         }
  786.         thisRoom = curRoom;
  787.     }
  788.     }
  789.  
  790.     strCpy(oldTarget, roomTab[roomTarg].rtname);
  791.  
  792.     pulledMLoc = roomBuf.msg[m].rbmsgLoc;
  793.     pulledMId  = roomBuf.msg[m].rbmsgNo ;
  794.  
  795.     for (i = m;  i > 0;  i--) {
  796.     roomBuf.msg[i].rbmsgLoc      = roomBuf.msg[i - 1].rbmsgLoc;
  797.     roomBuf.msg[i].rbmsgNo         = roomBuf.msg[i - 1].rbmsgNo ;
  798.     }
  799.     roomBuf.msg[0].rbmsgNo   = 0l;    /* mark new slot at end as free */
  800.     roomBuf.msg[0].rbmsgLoc  = 0 ;    /* mark new slot at end as free */
  801.  
  802.     noteRoom();
  803.     putRoom(ourRoom = thisRoom);
  804.     getRoom(roomTarg);
  805.  
  806.     note2Message(pulledMId, pulledMLoc);
  807.     putRoom(thisRoom);
  808.     noteRoom();
  809.     getRoom(ourRoom);
  810.     sPrintf(
  811.     msgBuf.mbtext,
  812.     "Following message from %s moved from %s> to %s> by %s",
  813.     (msgBuf.mbauth[0]) ? msgBuf.mbauth : "<anonymous>",
  814.     roomBuf.rbname,
  815.     oldTarget,
  816.     logBuf.lbname
  817.     );
  818.     aideMessage( /* noteDeletedMessage == */ TRUE);
  819.     return TRUE;
  820. }
  821.  
  822. /************************************************************************/
  823. /*    mPeek() dumps a sector in message.buf.    sysop debugging tool    */
  824. /************************************************************************/
  825. mPeek()
  826. {
  827. #ifdef TEST_SYS
  828.     char visible();
  829.     char blup[50];
  830.     unsigned char peekBuf[SECTSIZE];
  831.     int  col, row;
  832.     ulong r, s;
  833.  
  834.     sPrintf(blup, " sector to dump (between 0 - %d): ", cfg.maxMSector);
  835.     s = getNumber(blup, 0l, (ulong) (cfg.maxMSector-1));
  836.     r = s * SECTSIZE;
  837.     fseek(msgfl, r, 0);
  838.     fread(peekBuf, SECTSIZE, 1, msgfl);
  839.     crypte(peekBuf, SECTSIZE, 0);
  840.     for (row = 0;  row < 2;  row++) {
  841.     printf("\n ");
  842.     for (col = 0;  col < 64;  col++) {
  843.         printf("%c", visible(peekBuf[row*64 +col]));
  844.     }
  845.     }
  846. #else
  847.     printf("Disabled\n");
  848. #endif
  849. }
  850.  
  851. /************************************************************************/
  852. /*    msgToDisk() Puts a message to the given disk file        */
  853. /************************************************************************/
  854. msgToDisk(filename, id, loc)
  855. char *filename;
  856. ulong     id;
  857. unsigned loc;
  858. {
  859.     FILE *safeopen();
  860.  
  861.     if ((upfd = safeopen(filename, "a")) == NULL) {
  862.     mPrintf("ERROR: Couldn't open output file %s\n ", filename);
  863.     }
  864.     else {
  865.     outPut = DISK;
  866.     printMessage(loc, id);
  867.     outPut = NORMAL;
  868.     fclose(upfd);
  869.     }
  870. }
  871.  
  872. /************************************************************************/
  873. /*    mspr() -- special function for this edition so we can be    */
  874. /*          semi-intelligent in formatting.            */
  875. /************************************************************************/
  876. #ifndef _C86_BIG
  877. mspr(recip, start, length)
  878. char     **recip, *start;
  879. unsigned length;
  880. {
  881.     while (length-- != 0)
  882.         *(*recip)++ = *start++;
  883.     **recip = 0;
  884. }
  885. #else
  886. static mspr(recip, start, length)
  887. unsigned recip;
  888. unsigned char *start;
  889. unsigned length;
  890. {
  891.     movmem(start, *starr[recip],length);
  892.     *starr[recip] += length;
  893.     **starr[recip]= 0;
  894. }
  895. #endif
  896.  
  897. /************************************************************************/
  898. /*    mPrintf() formats format+args to modem and console        */
  899. /************************************************************************/
  900. #ifndef _C86_BIG
  901. mPrintf(format, args)
  902. unsigned char *format;
  903. unsigned      args;
  904. {
  905.     int  mspr();
  906.     char mAbort();
  907.     char string[MAXWORD], *s;
  908.  
  909.     /* Here's all the fun, these 2 lines.  Each C does it different */
  910.     s = string;
  911.     _fmtout(&mspr, &s, format, &args);        /* C86 internal */
  912.     /* Ain't compatibility fun? */
  913.  
  914.     mFormat(string);
  915. }
  916. #else
  917. mPrintf(format, args)
  918. unsigned char *format;
  919. unsigned      args;
  920. {
  921.     unsigned char string[MAXWORD], *s;
  922.     s = string;
  923.   starr[j]=&s;
  924.   if(j>4){
  925.     crashout("Big Model mPrintf crash!");
  926.   }
  927.   _fmtout(&mspr,j++,format,&args);
  928.   --j;
  929.   mFormat(string);
  930. }
  931. #endif
  932.  
  933. /************************************************************************/
  934. /*    splitF() formats format+args to file and console        */
  935. /************************************************************************/
  936. #ifndef _C86_BIG
  937. splitF(diskFile, format, args)
  938. FILE          *diskFile;
  939. unsigned char *format;
  940. unsigned      args;
  941. {
  942.     int  mspr();
  943.     char mAbort();
  944.     char string[MAXWORD], *s;
  945.  
  946.     /* Here's all the fun, these 2 lines.  Each C does it different */
  947.     s = string;
  948.     _fmtout(&mspr, &s, format, &args);        /* C86 internal */
  949.     /* Ain't compatibility fun? */
  950.  
  951.     printf(string);
  952.     if (diskFile != NULL) {
  953.     fprintf(diskFile, string);
  954.     fflush(diskFile);
  955.     }
  956. }
  957. #else
  958. splitF(diskFile, format, args)
  959. FILE          *diskFile;
  960. unsigned char *format;
  961. unsigned      args;
  962. {
  963.     unsigned char string[MAXWORD], *s;
  964.     s = string;
  965.   starr[j]=&s;
  966.   if(j>4){
  967.     crashout("Big Model mPrintf crash!");
  968.   }
  969.   _fmtout(&mspr,j++,format,&args);
  970.   --j;
  971.     printf(string);
  972.     if (diskFile != NULL) {
  973.     fprintf(diskFile, string);
  974.     fflush(diskFile);
  975.     }
  976. }
  977. #endif
  978.  
  979.  
  980.  
  981. /************************************************************************/
  982. /*    mWCprintf() formats format+args to sendWCChar()         */
  983. /************************************************************************/
  984. mWCprintf(format, args)
  985. char     *format;
  986. unsigned args;
  987. {
  988.     int  dPree();
  989.  
  990.     _fmtout(&dPree, 1, format, &args);          /* C86 internal */
  991.     sendWCChar(0);    /* Send NULL since it did before */
  992. }
  993.  
  994. /************************************************************************/
  995. /*    noteLogMessage() slots message into log record            */
  996. /************************************************************************/
  997. noteLogMessage(lBuf, logNo)
  998. struct logBuffer   *lBuf;
  999. int           logNo;
  1000. {
  1001.     int i;
  1002.  
  1003.     /* store into recipient's log record: */
  1004.     /* slide message pointers down to make room for this one: */
  1005.     for (i = 0;  i < MAILSLOTS - 1;  i++) {
  1006.     lBuf->lbslot[i]   = lBuf->lbslot[i + 1];
  1007.     lBuf->lbId[  i]   = lBuf->lbId[  i + 1];
  1008.     }
  1009.  
  1010.     /* slot this message in:    */
  1011.     lBuf->lbId[MAILSLOTS-1]      = cfg.newest;
  1012.     lBuf->lbslot[MAILSLOTS-1]      = cfg.catSector;
  1013.  
  1014.     putLog(lBuf, logNo);
  1015. }
  1016.  
  1017. /************************************************************************/
  1018. /*    noteMessage() slots message into current room            */
  1019. /************************************************************************/
  1020. noteMessage(lBuf, logNo)
  1021. struct logBuffer   *lBuf;
  1022. int           logNo;
  1023. {
  1024.     struct logBuffer lBuf2;
  1025.     int logRover;
  1026.     char *fn;
  1027.     char *findArchiveName();
  1028.  
  1029.     logBuf.lbvisit[0]    = ++cfg.newest;
  1030.  
  1031.     if (thisRoom != MAILROOM) {
  1032.     note2Message(cfg.newest, cfg.catSector);
  1033. /*splitF("N1 \n");*/
  1034. check();
  1035.  
  1036.     /* write it to disk:        */
  1037.     putRoom(thisRoom);
  1038. /*splitF("N2 \n");*/
  1039. check();
  1040.     noteRoom();
  1041. /*splitF("N3 \n");*/
  1042. check();
  1043.     } else {
  1044.     if (strCmpU(msgBuf.mbto, "Sysop") == SAMESTRING)  {
  1045.        if (!msgBuf.mbaddr[0]) {
  1046.            getRoom(AIDEROOM);
  1047.  
  1048.            /* enter in Aide> room -- 'sysop' is special */
  1049.            note2Message(cfg.newest, cfg.catSector);
  1050.  
  1051.            /* write it to disk:           */
  1052.            putRoom(AIDEROOM);
  1053.            noteRoom();
  1054.  
  1055.            getRoom(MAILROOM);
  1056.         }
  1057.         /* note in ourself if logged in: */
  1058.         if (loggedIn) {
  1059.         noteLogMessage(&logBuf, thisLog);
  1060.         fillMailRoom();
  1061.         }
  1062.     } else if (strCmpU(msgBuf.mbto, "Citadel") == SAMESTRING &&
  1063.            !msgBuf.mbaddr[0]) {
  1064.         for (logRover = 0; logRover < cfg.MAXLOGTAB; logRover++) {
  1065.         printf("Log %d\r", logRover);        /* Notify sysop    */
  1066.         getLog(&lBuf2, logRover);
  1067.         if (lBuf2.lbflags.L_INUSE) {
  1068.             noteLogMessage(&lBuf2, logRover);
  1069.         }
  1070.         }
  1071.         noteLogMessage(&logBuf, thisLog);        /* note in ourself    */
  1072.         fillMailRoom();                /* update room also */
  1073.     } else {
  1074.         if (logNo != thisLog && !msgBuf.mbaddr[0])    {
  1075.         noteLogMessage(lBuf, logNo);        /* note in recipient*/
  1076.         }
  1077.         if (loggedIn) {
  1078.         noteLogMessage(&logBuf, thisLog);   /* note in ourself    */
  1079.         fillMailRoom();             /* update room also */
  1080.         }
  1081.     }
  1082.     }
  1083.  
  1084. /*splitF("N4 \n");*/
  1085. check();
  1086.     msgBuf.mbaddr[0] = 0;
  1087.     msgBuf.mbto[0]   = 0;
  1088. /*splitF("N5 \n");*/
  1089. check();
  1090.  
  1091.     /* make message official:    */
  1092.     cfg.catSector   = mFile1.thisSector;
  1093.     cfg.catChar     = mFile1.thisChar;
  1094. /*splitF("N6 \n");*/
  1095. check();
  1096.     setUp(FALSE);
  1097. /*splitF("N7 \n");*/
  1098. check();
  1099.     if (roomBuf.rbflags.ARCHIVE == 1) {
  1100. /*splitF("N8 \n");*/
  1101. check();
  1102.     if ((fn = findArchiveName(thisRoom)) == NULL) {
  1103.         sPrintf(msgBuf.mbtext, "Integrity problem with Archiving: %s.",
  1104.                         roomBuf.rbname);
  1105.         aideMessage(FALSE);
  1106. /*splitF("N9 \n");*/
  1107. check();
  1108.     }
  1109.     else {
  1110.         msgToDisk(fn, roomBuf.msg[MSGSPERRM - 1].rbmsgNo,
  1111.               roomBuf.msg[MSGSPERRM - 1].rbmsgLoc);
  1112. /*splitF("N10\n");*/
  1113. check();
  1114.     }
  1115. /*splitF("N11\n");*/
  1116. check();
  1117.     }
  1118. /*splitF("N12\n");*/
  1119. check();
  1120. }
  1121.  
  1122. /************************************************************************/
  1123. /*    note2Message() makes slot in current room... called by noteMess */
  1124. /************************************************************************/
  1125. note2Message(id, loc)
  1126. ulong     id;
  1127. unsigned loc;
  1128. {
  1129.     int  i;
  1130.  
  1131.     /* store into current room: */
  1132.     /* slide message pointers down to make room for this one:        */
  1133.     for (i = 0;  i < MSGSPERRM - 1;  i++) {
  1134.     roomBuf.msg[i].rbmsgLoc  = roomBuf.msg[i+1].rbmsgLoc;
  1135.     roomBuf.msg[i].rbmsgNo     = roomBuf.msg[i+1].rbmsgNo ;
  1136.     }
  1137.  
  1138.     /* slot this message in:        */
  1139.     roomBuf.msg[MSGSPERRM-1].rbmsgNo     = id ;
  1140.     roomBuf.msg[MSGSPERRM-1].rbmsgLoc     = loc;
  1141. }
  1142.  
  1143. /************************************************************************/
  1144. /*    findMessage() gets all set up to do something with a message    */
  1145. /************************************************************************/
  1146. findMessage(loc, id)
  1147. unsigned loc;         /* sector in message.buf         */
  1148. ulong     id;         /* unique-for-some-time ID#     */
  1149. {
  1150.     ulong here;
  1151.  
  1152.     startAt(msgfl, &mFile1, loc, 0);
  1153.  
  1154.     do {
  1155.     getMessage();
  1156.     sscanf(msgBuf.mbId, "%ld", &here);
  1157. /*    here = atol(msgBuf.mbId);    */
  1158.     } while (here != id &&  mFile1.thisSector == loc);
  1159.  
  1160.     return ((here == id));
  1161. }
  1162.  
  1163. /************************************************************************/
  1164. /*    printMessage() prints indicated message on modem & console    */
  1165. /************************************************************************/
  1166. printMessage(loc, id)
  1167. unsigned loc;         /* sector in message.buf         */
  1168. ulong     id;         /* unique-for-some-time ID#     */
  1169. {
  1170.     char  dGetWord();
  1171.     char  c, moreFollows;
  1172.     ulong here;
  1173.     long val, atoi();
  1174.     int oldTermWidth;
  1175.     int strip;
  1176.  
  1177.     if (!findMessage(loc, id) &&  usingWCprotocol == WC_NONE) {
  1178.     mPrintf("?can't find message! Looking for %lu in sector %u!\n ",
  1179.             id, loc);
  1180.     if (cfg.debug)
  1181.         printf(" loc=%u, id=%lu, mbIds=%s, here=%lu\n",
  1182.         loc, id, msgBuf.mbId, here
  1183.         );
  1184.  
  1185.     return;
  1186.     }
  1187.  
  1188.     if (usingWCprotocol != WC_NETWORK) {
  1189.     oldTermWidth = termWidth;
  1190.     if (outPut == DISK) {
  1191.         termWidth = 80;
  1192.     }
  1193.     doCR();
  1194.  
  1195.     if (msgBuf.mbdate[ 0])       mPrintf("   %s ", msgBuf.mbdate);
  1196.     if (msgBuf.mbtime[ 0] && sendTime) mPrintf("%s ", msgBuf.mbtime);
  1197.     if (msgBuf.mbauth[ 0]) {
  1198.         mPrintf(    "from %s",    msgBuf.mbauth );
  1199.     }
  1200.     if (msgBuf.mboname[0])        mPrintf(    " @%s",       msgBuf.mboname);
  1201.     if (strCmpU(msgBuf.mbroom, roomBuf.rbname) != SAMESTRING) {
  1202.         mPrintf(" in %s>",      msgBuf.mbroom );
  1203.     }
  1204.     if (msgBuf.mbto[   0])        mPrintf(    " to %s",     msgBuf.mbto   );
  1205.     if (msgBuf.mbaddr[ 0] &&
  1206.         strCmpU(msgBuf.mbaddr, R_SH_MARK) != SAMESTRING)
  1207.                     mPrintf(    " (on %s)",   msgBuf.mbaddr );
  1208.  
  1209.     doCR();
  1210.  
  1211.     while (1) {
  1212.         moreFollows     = dGetWord(msgBuf.mbtext, 150);
  1213.         /* strip control Ls out of the output            */
  1214.         for (strip = 0; msgBuf.mbtext[strip] != 0; strip++)
  1215.         if (msgBuf.mbtext[strip] == 0x0c ||
  1216.             msgBuf.mbtext[strip] == SPECIAL)
  1217.             msgBuf.mbtext[strip] = ' ';
  1218.         putWord(msgBuf.mbtext);
  1219.         if (!(moreFollows  &&  !mAbort())) {
  1220.         if (outFlag == OUTNEXT)     /* If <N>ext, extra line */
  1221.             doCR();
  1222.         break;
  1223.         }
  1224.     }
  1225.     doCR();
  1226.     termWidth = oldTermWidth;
  1227.     }
  1228.     else {
  1229.     /* networking dump of message: */
  1230.  
  1231.     /* fill in local node in origin fields if local message: */
  1232.     if (!msgBuf.mborig[ 0])
  1233.         strCpy(msgBuf.mborig,  cfg.nodeId + cfg.codeBuf  );
  1234.     if (!msgBuf.mboname[0])
  1235.         strCpy(msgBuf.mboname, cfg.nodeName + cfg.codeBuf);
  1236.  
  1237.     /* Convert # to 8-bit Citadel style for compatibility    */
  1238.     if (!msgBuf.mbsrcId[0]) {
  1239.         val = atoi(msgBuf.mbId);
  1240.         sPrintf(msgBuf.mbsrcId, "%ld %ld",
  1241.               val & 0xFFFF0000l,val & 0xFFFFl);
  1242.     }
  1243.  
  1244.     /* send header fields out: */
  1245.     if (msgBuf.mbauth[ 0])    mWCprintf("A%s", msgBuf.mbauth );
  1246.     if (msgBuf.mbdate[ 0])    mWCprintf("D%s", msgBuf.mbdate );
  1247.     if (msgBuf.mbtime[ 0])    mWCprintf("C%s", msgBuf.mbtime );
  1248.     if (msgBuf.mboname[0])    mWCprintf("N%s", msgBuf.mboname);
  1249.     if (msgBuf.mborig[ 0])    mWCprintf("O%s", msgBuf.mborig );
  1250.     if (msgBuf.mbroom[ 0])    mWCprintf("R%s", msgBuf.mbroom );
  1251.     if (msgBuf.mbsrcId[0])    mWCprintf("S%s", msgBuf.mbsrcId);
  1252.     if (msgBuf.mbto[   0])    mWCprintf("T%s", msgBuf.mbto   );
  1253.  
  1254.     /* send message text proper: */
  1255.     sendWCChar('M');
  1256.     do  {
  1257.         c = getMsgChar();
  1258.         if (c=='\n')  c='\r';
  1259.         if (!sendWCChar(c)) break;
  1260.     } while (c);
  1261.     }
  1262.     msgBuf.mbaddr[0] = 0;
  1263. }
  1264.  
  1265. /************************************************************************/
  1266. /*    pullIt() is a sysop special to remove a message from a room    */
  1267. /************************************************************************/
  1268. pullIt(m)
  1269. int m;
  1270. {
  1271.     char  finished;
  1272.     char  answer;
  1273.  
  1274.     /* confirm that we're removing the right one:    */
  1275.     outFlag = OUTOK;
  1276.     printMessage(roomBuf.msg[m].rbmsgLoc, roomBuf.msg[m].rbmsgNo);
  1277.  
  1278.     for (finished = FALSE; !finished;)    {
  1279.     do {
  1280.         outFlag = IMPERVIOUS;
  1281.         mPrintf("\n <D>elete <M>ove <A>bort? (D/M/A) ");
  1282.         answer = toUpper(iChar());
  1283.         if (answer == 'A' ||
  1284.         answer == 'D' ||
  1285.         answer == 'M')
  1286.         break;
  1287.     } while (onLine());
  1288.  
  1289.     outFlag = OUTOK;
  1290.     if (answer == 'A' || !onLine()) return FALSE;
  1291.  
  1292.     if (answer == 'D')    finished = deleteMessage(m);
  1293.     else if (answer == 'M') finished = moveMessage(m);
  1294.     }
  1295.     return TRUE;
  1296. }
  1297.  
  1298. /************************************************************************/
  1299. /*    putMessage() stores a message to disk                */
  1300. /*    Always called before noteMessage() -- newest not ++ed yet.    */
  1301. /*    Returns: TRUE on successful save, else FALSE            */
  1302. /************************************************************************/
  1303. char putMessage()
  1304. {
  1305.     extern char *ALL_LOCALS, *WRITE_LOCALS;
  1306.     extern ulong roomHiMsgs[];
  1307.     char  *s, *month, *ml, *netAddress;
  1308.     int   year, day, h, m, netPlace;
  1309.  
  1310.     startAt(msgfl, &mFile1, cfg.catSector, cfg.catChar);
  1311.                     /* tell putMsgChar where to write    */
  1312.     if (cfg.mirror)
  1313.     startAt(msgfl2, &mFile2, cfg.catSector, cfg.catChar);
  1314.  
  1315.     putMsgChar(0xFF);            /* start-of-message         */
  1316.  
  1317.     /* write message ID */
  1318.     dPrintf("%lu", cfg.newest + 1);
  1319.     putMsgChar(0);
  1320.  
  1321.     /* write date:    */
  1322.     getdate(&year, &month, &day, &h, &m);
  1323.     dPrintf("D%d%s%02d", year, month, day);
  1324.     putMsgChar(0);
  1325.  
  1326.     /* write time:    */
  1327.     if (h >= 12)
  1328.     ml = "pm";
  1329.     else
  1330.     ml = "am";
  1331.     if (h >= 13)
  1332.     h -= 12;
  1333.     if (h == 0)
  1334.     h = 12;
  1335.     dPrintf("C%d:%02d %s", h, m, ml);
  1336.     putMsgChar(0);
  1337.  
  1338.     /* write room name out:        */
  1339.     dPrintf("R%s", roomBuf.rbname);
  1340.     putMsgChar(0);
  1341.  
  1342.     if (loggedIn || strCmpU(msgBuf.mbauth, "Citadel") == SAMESTRING) {
  1343.     /* write author's name out:        */
  1344.     dPrintf("A%s", msgBuf.mbauth);
  1345.     putMsgChar(0);        /* null to end string   */
  1346.     }
  1347.  
  1348.     if (msgBuf.mbto[0]) {    /* private message -- write addressee    */
  1349.     dPrintf("T%s", msgBuf.mbto);
  1350.     putMsgChar(0);
  1351.     }
  1352.  
  1353.     if (msgBuf.mbaddr[0]) {    /* net message routing            */
  1354.     if (strCmpU(msgBuf.mbaddr, ALL_LOCALS) == SAMESTRING) {
  1355.         for (netPlace = 0; netPlace < cfg.netSize; netPlace++) {
  1356.         getNet(netPlace);
  1357.         if (netBuf.nbflags.in_use && netBuf.nbflags.local)
  1358.             netMailProcess(netPlace);
  1359.         }
  1360.     }
  1361.     else if (strCmpU(msgBuf.mbaddr, R_SH_MARK) == SAMESTRING) {
  1362.         dPrintf("N%s", msgBuf.mboname);
  1363.         putMsgChar(0);
  1364.         roomHiMsgs[thisRoom] = cfg.newest+1;
  1365.     }
  1366.     else {
  1367.         netPlace = searchNameNet(msgBuf.mbaddr);
  1368.         getNet(netPlace);
  1369.         netMailProcess(netPlace);
  1370.     }
  1371.     dPrintf("Q%s", wrNetId(msgBuf.mbaddr));
  1372.     putMsgChar(0);
  1373.     }
  1374.  
  1375.     /* write message text by hand because it would overrun dPrintf buffer: */
  1376.     putMsgChar('M');    /* M-for-message.    */
  1377.     for (s = msgBuf.mbtext;  *s;  s++) putMsgChar(*s);
  1378.  
  1379.     putMsgChar(0);    /* null to end text        */
  1380.     flushMsgBuf();
  1381.  
  1382.     return  TRUE;
  1383. }
  1384.  
  1385. /************************************************************************/
  1386. /*    netMailProcess() Process net mail message            */
  1387. /************************************************************************/
  1388. netMailProcess(netPlace)
  1389. int netPlace;
  1390. {
  1391.     if (!netBuf.nbflags.local) {
  1392.     if (logBuf.credit == 0) {
  1393.         crashout("Someone obtained illegal l-d networking privs!");
  1394.     }
  1395.     logBuf.credit--;
  1396.     storeLog();
  1397.     }
  1398.     netMailOut();
  1399.     putNet(netPlace);
  1400. }
  1401.  
  1402. /************************************************************************/
  1403. /*    netMailOut() put mail pointer and number into temp file     */
  1404. /************************************************************************/
  1405. netMailOut()
  1406. {
  1407.     FILE  *safeopen(), *fd;
  1408.     label fn;
  1409.     struct netMLstruct buf;
  1410.  
  1411.     sPrintf(fn, "a:%d.ml", thisNet);
  1412.     fn[0] += cfg.netDisk;
  1413.     if ((fd = safeopen(fn, "ab")) == NULL) {
  1414.     crashout("putMessage -- couldn't open mail router!");
  1415.     }
  1416.     buf.ML_id  = cfg.newest + 1;
  1417.     buf.ML_loc = cfg.catSector;
  1418.     putMLNet(fd, buf);
  1419.     fclose(fd);
  1420.     netBuf.nbflags.normal_mail = TRUE;
  1421. }
  1422.  
  1423. /************************************************************************/
  1424. /*    putMsgChar() writes successive message chars to disk        */
  1425. /*    Globals:    thisChar=    thisSector=            */
  1426. /*    Returns:    ERROR if problems else TRUE            */
  1427. /************************************************************************/
  1428. int putMsgChar(c)
  1429. unsigned char c;
  1430. {
  1431.     int  toReturn;
  1432.     int  count1, count2;
  1433.     long temp;
  1434.  
  1435.     toReturn = TRUE;
  1436.     count1 = doActualWrite(msgfl, &mFile1, c);
  1437.     if (cfg.mirror) {
  1438.     count2 = doActualWrite(msgfl2, &mFile2, c);
  1439.     if (count1 != count2) printf("Mirror msg count discrepancy!");
  1440.     }
  1441.     if (count1)
  1442.     logBuf.lbvisit[(MAXVISIT-1)]    = ++cfg.oldest;
  1443.     return toReturn;
  1444. }
  1445.  
  1446. /************************************************************************/
  1447. /*    putNetMessage() stores a message to disk            */
  1448. /*    Always called before noteMessage() -- newestLo not ++ed yet.    */
  1449. /*    Returns: TRUE on successful save, else FALSE.            */
  1450. /************************************************************************/
  1451. putNetMessage()
  1452. {
  1453.     char *s;
  1454.  
  1455.     startAt(msgfl, &mFile1, cfg.catSector, cfg.catChar);
  1456.                     /* tell putMsgChar where to write    */
  1457.     if (cfg.mirror)
  1458.     startAt(msgfl2, &mFile2, cfg.catSector, cfg.catChar);
  1459.  
  1460.     putMsgChar(0xFF);            /* start-of-message         */
  1461.  
  1462.     /* write message ID */
  1463.     dPrintf("%lu", cfg.newest + 1);
  1464.     putMsgChar(0);
  1465.  
  1466.     /* write date:    */
  1467.     dPrintf("D%s", tempMess.mbdate);
  1468.     putMsgChar(0);
  1469.  
  1470.     /* write room name out:        */
  1471.     dPrintf("R%s", roomBuf.rbname);
  1472.     putMsgChar(0);
  1473.  
  1474.     /* write source node name out:    */
  1475.     dPrintf("N%s", tempMess.mboname);
  1476.     putMsgChar(0);
  1477.  
  1478.     /* write source node id out:    */
  1479.     dPrintf("O%s", tempMess.mborig);
  1480.     putMsgChar(0);
  1481.  
  1482.     /* write source id out:        */
  1483.     dPrintf("S%s", tempMess.mbsrcId);
  1484.     putMsgChar(0);
  1485.  
  1486.     if (tempMess.mbauth[0]) {
  1487.     /* write author's name out:        */
  1488.     dPrintf("A%s", tempMess.mbauth);
  1489.     putMsgChar(0);        /* null to end string   */
  1490.     }
  1491.  
  1492.     if (tempMess.mbto[0]) {      /* private message -- write addressee   */
  1493.     dPrintf("T%s", tempMess.mbto);
  1494.     putMsgChar(0);
  1495.     }
  1496.  
  1497.     if (tempMess.mbtime[0]) {
  1498.     dPrintf("C%s", tempMess.mbtime);
  1499.     putMsgChar(0);
  1500.     }
  1501.  
  1502.     /* write message text by hand because it would overrun dPrintf buffer: */
  1503.     putMsgChar('M');    /* M-for-message.    */
  1504.     for (s=tempMess.mbtext;  *s;  s++) putMsgChar(*s);
  1505.  
  1506.     putMsgChar(0);    /* null to end text        */
  1507.     flushMsgBuf();
  1508. }
  1509.  
  1510. /************************************************************************/
  1511. /*    putWord() writes one word to modem & console            */
  1512. /************************************************************************/
  1513. putWord(st)
  1514. char *st;
  1515. {
  1516.     char *s;
  1517.     int  newColumn;
  1518.  
  1519.     for (newColumn = crtColumn, s = st;  *s; s++)   {
  1520.     if (*s != TAB)    ++newColumn;
  1521.     else        while (++newColumn % 8);
  1522.     }
  1523.     if (newColumn > termWidth)     doCR();
  1524. /*  if (newColumn > logPtr->lbwidth)   doCR();        */
  1525.  
  1526.     for (;  *st;  st++) {
  1527.  
  1528.     if (*st != TAB) ++crtColumn;
  1529.     else        while (++crtColumn % 8);
  1530.  
  1531.     /* worry about words longer than a line:    */
  1532.     if (crtColumn > termWidth) doCR();
  1533. /*    if (crtColumn > logPtr->lbwidth) doCR();    */
  1534.  
  1535.     if (prevChar!=NEWLINE  ||  (*st > ' '))   oChar(*st);
  1536.     else {
  1537.         /* end of paragraph: */
  1538.         if (outFlag == OUTPARAGRAPH)   {
  1539.         outFlag = OUTOK;
  1540.         }
  1541.         doCR();
  1542.         oChar(*st);
  1543.     }
  1544.     }
  1545. }
  1546.  
  1547. /************************************************************************/
  1548. /*    showMessages() is routine to print roomful of msgs        */
  1549. /************************************************************************/
  1550. showMessages(whichMess, revOrder)
  1551. char whichMess, revOrder;
  1552. {
  1553.     int   i;
  1554.     int   start, finish, increment;
  1555.     ulong lowLim, highLim, msgNo;
  1556.     char  pulled;
  1557.     char  jrnlPrompt[130];
  1558.     char  fullFileName[100];
  1559.  
  1560.     setUp(FALSE);
  1561.  
  1562.     if (thisRoom == MAILROOM && !loggedIn) {
  1563.     tutorial("POLICY.HLP");
  1564.     return ;
  1565.     }
  1566.  
  1567.     if (!expert && usingWCprotocol == WC_NONE)
  1568.         mPrintf("\n <J>ump <N>ext <P>ause <S>top");
  1569.  
  1570.     if (whichIO != CONSOLE && thisRoom == MAILROOM) echo = CALLER;
  1571.  
  1572.     /* Allow for reverse retrieval: */
  1573.     if (!revOrder) {
  1574.     start        = 0;
  1575.     finish        = MSGSPERRM;
  1576.     increment   = 1;
  1577.     } else {
  1578.     start        = (MSGSPERRM -1);
  1579.     finish        = -1;
  1580.     increment   = -1;
  1581.     }
  1582.  
  1583.     switch (whichMess)     {
  1584.     case NEWoNLY:
  1585.     lowLim    = logBuf.lbvisit[ logBuf.lbgen[thisRoom] & CALLMASK]+1;
  1586.     highLim = cfg.newest;
  1587.     if (!revOrder && usingWCprotocol == WC_NONE &&
  1588.                    thisRoom != MAILROOM && oldToo) {
  1589.             for (i = MSGSPERRM - 1; i != -1; i--)
  1590.                 if (lowLim > roomBuf.msg[i].rbmsgNo &&
  1591.                     roomBuf.msg[i].rbmsgNo >= cfg.oldest)
  1592.                     break;
  1593.             if (i != -1)
  1594.         printMessage(roomBuf.msg[i].rbmsgLoc, roomBuf.msg[i].rbmsgNo);
  1595.         }
  1596.     break;
  1597.     case OLDaNDnEW:
  1598.     lowLim    = cfg.oldest;
  1599.     highLim = cfg.newest;
  1600.     break;
  1601.     case OLDoNLY:
  1602.     lowLim    = cfg.oldest;
  1603.     highLim = logBuf.lbvisit[ logBuf.lbgen[thisRoom] & CALLMASK];
  1604.     break;
  1605.     }
  1606.  
  1607.     /* stuff may have scrolled off system unseen, so: */
  1608.     if (cfg.oldest  > lowLim) {
  1609.     lowLim = cfg.oldest;
  1610.     }
  1611.  
  1612.     for (i = start; i != finish && onLine(); i += increment) {
  1613.     if (outFlag != OUTOK) {
  1614.         if (outFlag == OUTNEXT || outFlag == OUTPARAGRAPH)
  1615.         outFlag = OUTOK;
  1616.         else if (outFlag == OUTSKIP)   {
  1617.         echo = BOTH;
  1618.         return;
  1619.         }
  1620.     }
  1621.  
  1622.     msgNo    = roomBuf.msg[i].rbmsgNo;
  1623.     if (
  1624.         msgNo >= lowLim
  1625.         &&
  1626.         highLim >= msgNo
  1627.      ) {
  1628.         printMessage(roomBuf.msg[i].rbmsgLoc, msgNo);
  1629.  
  1630.         /*    Pull current message from room if flag set */
  1631.         if (pullMessage) {
  1632.         pullMessage = FALSE;
  1633.         pulled = pullIt(i);
  1634.         outFlag = OUTOK;
  1635.         if (revOrder && pulled)   i++;
  1636.         }
  1637.         else
  1638.         pulled = FALSE;
  1639.  
  1640.         if (journalMessage) {
  1641.         exChar = ESC;
  1642.         if (strLen(jrnlFile) != 0)
  1643.             sPrintf(fullFileName, " (C/R = '%s', ", jrnlFile);
  1644.         else
  1645.             strCpy(fullFileName, " (");
  1646.         strCat(fullFileName, "ESCape to abort)");
  1647.         sPrintf(jrnlPrompt, "%s%s", strFile, fullFileName);
  1648.         getString(jrnlPrompt, fullFileName, 100, TRUE, ECHO);
  1649.         exChar = '?';
  1650.         if (fullFileName[0] != ESC &&
  1651.                (strLen(fullFileName) != 0 || strLen(jrnlFile) != 0)) {
  1652.  
  1653.             if (strLen(fullFileName) == 0)
  1654.             strCpy(fullFileName, jrnlFile);
  1655.             msgToDisk(fullFileName, msgNo, roomBuf.msg[i].rbmsgLoc);
  1656.             strCpy(jrnlFile, fullFileName);
  1657.         }
  1658.         outFlag = OUTOK;
  1659.         journalMessage = FALSE;
  1660.         }
  1661.  
  1662.         if (
  1663.         usingWCprotocol == WC_NONE
  1664.         &&
  1665.         !pulled
  1666.         &&
  1667.         thisRoom  == MAILROOM
  1668.         &&
  1669.         whichMess == NEWoNLY
  1670.         &&
  1671.         msgBuf.mborig[0] == 0    /* i.e. is local mail        */
  1672.         &&
  1673.         strCmpU(msgBuf.mbauth, logBuf.lbname) != SAMESTRING
  1674.         &&
  1675.         strCmpU(msgBuf.mbauth, "Citadel") != SAMESTRING
  1676.         &&
  1677.         getYesNo("respond")
  1678.         ) {
  1679.         if (replyMessage())
  1680.             i--;
  1681.         if (whichIO != CONSOLE && thisRoom == MAILROOM)
  1682.             echo = CALLER;    /* Restore privacy zapped by make... */
  1683.         outFlag = OUTOK;
  1684.         }
  1685.     }
  1686.     }
  1687.     echo = BOTH;
  1688. }
  1689.  
  1690. /************************************************************************/
  1691. /*    startAt() sets location to begin reading message from        */
  1692. /************************************************************************/
  1693. startAt(whichmsg, mFile, sect, byt)
  1694. unsigned sect;
  1695. int     byt;
  1696. FILE     *whichmsg;
  1697. struct mBuf *mFile;
  1698. {
  1699.     long temp;
  1700.  
  1701.     GMCCache  = '\0';    /* cache to unGetMsgChar() into */
  1702.  
  1703.     if (sect >= cfg.maxMSector) {
  1704.     printf("?startAt s=%u,b=%d", sect, byt);
  1705.     crashout("?startAt crash");
  1706.     }
  1707.     mFile->thisChar    = byt;
  1708.     mFile->thisSector  = sect;
  1709.  
  1710.     temp = sect;
  1711.     temp *= SECTSIZE;
  1712.     fseek(whichmsg, temp, 0);
  1713.     if (fread(mFile->sectBuf, SECTSIZE, 1, whichmsg) != 1) {
  1714.     crashout("?startAt read fail");
  1715.     }
  1716.     crypte(mFile->sectBuf, SECTSIZE, 0);
  1717. }
  1718.  
  1719. /************************************************************************/
  1720. /*    unGetMsgChar() returns (at most one) char to getMsgChar()    */
  1721. /************************************************************************/
  1722. unGetMsgChar(c)
  1723. unsigned char c;
  1724. {
  1725.     GMCCache    = c;
  1726. }
  1727.  
  1728. /************************************************************************/
  1729. /*    exportSector() returns current sector to write to        */
  1730. /************************************************************************/
  1731. unsigned exportSector()
  1732. {
  1733.     return mFile1.thisSector;
  1734. }
  1735.